/**
 *  Project Vorpal
 *  \file Compiler.h
 *  \author Created by Russell Webb
 */

#ifndef VORPAL_COMPILER_H
#define VORPAL_COMPILER_H

#include "Lexer.h"
#include "Assembler.h"

/*--------------------------------------------------------------------------
	A sketch of the Vorpal Grammar
----------------------------------------------------------------------------
	method: 
		"method" params code
	params:
		"(" [arg] ")"
	arg:
		id ["," arg]
	id:
		a-zA-Z_(a-zA-Z0-9_)*
	code:
		"{" ( assignment | expression | if | return | while )* "}"
	while:
		"while" "(" expression ")" code
	if:
		"if" "(" expression ")" code [ elseif ] [ else ]
	elseif:
		"elseif" "(" expression ")" code
	else:
		"else" code
	return:
		"return" "(" expression ")"
	expression:
		(unit [binop expression] | unit message [expression]) ["\n"]
	assignment:
		id "=" expression
	binop:
		(~!@#$%^&*()_)+
	unary:
		(~!@  #$%^&*()_)+
	message:
		"." id "(" [expression | expression "," expression] ")"
	unit:
		("sys" message) | ([unary] (id | number))
--------------------------------------------------------------------------*/

namespace Vorpal{

/**
 *  \class LocalNames 
 *
 *  \brief Stores local variable names. 
 *
 */

	class LocalNames{
	public:
		LocalNames() : numParams(0), numLocals(0){ }
		
		int CreateLocal(string name);

		int CreateParam(string name);
		
		void Clear();

		bool Get(string name, int *value = 0);
		
		int NumParams() const{ return numParams; }
		
	private:
		int numParams;
		int numLocals;
		map< string, int > names;
	};

/**  
 *   \class Compiler
 *   \brief Class used as an interface to interpret written code and operands into messages sent to the assembler. The assembler code is then read by the virtual machine.
 *   
 */

	class Compiler{
	public:
/**
 *   \fn Compiler() 
 *
 *   \brief The Constructor List creates LexerStates for all the types of tokens that the compiler might encounter. Then creates static arrays of keywords, operators, special operators and whitespace operators. Then loads StateRules onto each LexerState's rule stack. 
 */
		Compiler();
		
		void TestTokenizer(const char *path);
				
		bool Compile(const char *path);

		bool CompileString(const char *str);
		
		uint32_t Size() const{ return assemble.Size(); }

		void EnableDebugInfo( bool enable ){ recordDebugInfo = traceOpcodes || enable; }
		
		/*
		 * /brief put the opcodes, operands and debugging information into dest
		 */
		void Get(Cell *dest, uint32_t size){
			assemble.Get(dest, size);
		}
		
		void GlobalName(const char *name, bool active = true){
			globalNames[name] = active;
		}
		
	private:
		enum{
			// change Compiler::TokenLabel is you change these
			ERROR = 0,
			NONE,
			SIMPLE,
			OPERATOR,
			STRING,
			WORD,
			NUMBER,
			ASSIGN,
			// keywords
			HAS,
			DOES,
			IS,
			RETURN,
			IF,
			ELSEIF,
			ELSE,
			WHILE,
			BREAK,
			METHOD,
			NEW,  
			FOR,

			NumTokenTypes // must be last
		};
		
		const char *TokenLabel(int type);

		// ---------------------------------------------------------------------------
		// Helper methods for the parser
		// ---------------------------------------------------------------------------

		void Clear();

		void Need(bool match);

		void Need(int type, const char *simple = 0);
		
		bool Compare(uint32_t index, int type, const char *simple);
		
		bool Previous(int type, const char *simple = 0);

		bool Match(int type, const char *simple = 0);
		
		void Rewind(uint32_t numTokens);
		
		void AnyNewlines();
		
		void ReportParseError(const char *errString);

		bool FindSequenceOnLine(int type[], const char *simple[], uint32_t length);
		
		// ---------------------------------------------------------------------------
		// Following methods implement the recursive decent parser
		// ---------------------------------------------------------------------------
		
		bool Method();
		
		bool SubMethod();

		bool Parameters();

		bool VariableLengthParam(uint &paramIndex);

		bool Parameter();
		
		bool Simple(const char *options);
		
		bool PreviousSimple(const char *options);

		bool Brace(const char *which);

		bool OpenParen();
		
		bool CloseParen();
		
		bool CodeToBrace(bool *lastWasReturn = 0);

		bool While();
		
		bool For();
		
		bool Break();

		bool Return();

		bool If();
		
		bool ElseIf(char *startLabel, const char *finalLabel);
		
		bool Else();	
		
		bool DotMessage();
		
		bool Arguments(uint32_t *numArgs);

		bool Number();

		bool Assignment();

		bool LocalAssignment();

		bool SlotAssignment();
		
		bool Expression(bool dropAtNewline = true, bool expectNormalEnd = true);
		
		bool New();

		bool SingleUnit();
		
		bool SlotGet();
			
		bool SlotIdentifier();
		
		bool SlotSetLast(uint32_t *setIndex, bool *dynamicAssign);

		bool DynamicSlot();

		bool Comma();
		
		bool TableDefinition();
		
		// ---------------------------------------------------------------------------
		// Following methods call the Assembler to do the assembly during parsing
		// ---------------------------------------------------------------------------
		void UniqueLabel(char *label);		
		void AsmLabel(const char *label);
		void AsmJump(const char *label);
		void AsmJumpFalse(const char *label);
		void AsmNumber(uint32_t tokenIndex, bool negate);
		void AsmDrop();
		void AsmParameter(uint32_t index);
		void AsmStartMethod(uint32_t numArgs, uint32_t varParamIndex);
		void AsmEndMethod(uint32_t numLocals);
		void AsmReturn(bool returnTop);
		void AsmAssignLocal(uint32_t tokenIndex);
		void AsmIdentifier(uint32_t tokenIndex);
		void AsmString(uint32_t tokenIndex);
		void AsmMessage(uint32_t numArgs, uint32_t messageTokenIndex);
		void AsmBinary(uint32_t tokenIndex);
		void AsmUnary(uint32_t tokenIndex);
		void AsmObject(uint32_t numArgs);      
		void AsmSetObject(uint32_t keyTokenIndex);
		void AsmGetObject(uint32_t keyTokenIndex);
		void AsmGlobal(const char *name);
		void AsmCode(const Cell *code, uint32_t codeSize);
		void AsmRecordDebugInfo();
		
		void Preprocess();
		
		LocalNames localNames;
		Tokenizer tokens;
		uint32_t current;
		uint32_t numArgs;
		uint32_t numLocalsNeeded;
		uint32_t labelNum;
		bool recordDebugInfo;
				
		Assembler assemble;
		LexerState Begin;
		LexerState Minus;
		LexerState Comment;
		LexerState Decimal;
		LexerState Integer;
		LexerState Operator;
		LexerState String;
		LexerState StringDouble;
		LexerState Word;
		
		vector< const char* > break_to_here;
		map< string, bool > globalNames;
		const char *sourcePath;
	};

}

#endif
